﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using MySql.Data.MySqlClient;
using snat.model;
using snat.util;

namespace snat.proxy.sets {
    public class ListProxy<T> : System.Collections.Generic.IEnumerable<T>
            where T : Item {

        private int start = 0;
        private int bufferSize = 2;
        private int iteratorBufferSize = 200;

        private string sqlQuery;
        private readonly bool isGreedy;
        public bool IsGreedy {
            get { return isGreedy; }
        }

        public ListProxy(string _sqlQuery, bool _isGreedy = false) {
            this.sqlQuery = _sqlQuery;
            this.isGreedy = _isGreedy;
        }

        List<T> cache = null;
        public T this[int i] {
            get {
                //Contract.Requires<ArgumentNullException>(i >= 0 && i < Count, "i must be within the range of 0 and Count");
                if (cache != null && i >= start && i < start + bufferSize) {
                    return cache[i - start];
                } else {
                    Console.Write("Readloading the cache..");
                    DateTime startTime = DateTime.Now;
                    cache = GetItems(i, bufferSize);
                    TimeSpan elapsed = DateTime.Now - startTime;
                    Console.WriteLine("done.\t" + elapsed.Milliseconds + "ms");
                    start = i;
                    return cache[0];
                }
            }
        }

        private int? count = null;
        public int Count {
            get {
                if (count == null) {
                    try {
                        DataSet countSet = DataProxy.Instance.RunQuery(
                                @"SELECT COUNT(*) AS Count
                              FROM (" + sqlQuery + ") AS dummy");
                        count = (int)countSet.Tables["Table"].Rows[0].Field<Int64>("Count");
                    } catch (MySqlException ex) {
                        ErrorLog.Log(@"Bad SQL Query for COUNT:  SELECT COUNT(*) AS Count
                                                                 FROM (" + sqlQuery + ") AS dummy");
                        throw ex;
                    }
                }
                return (int)count;
            }
        }

        delegate List<T> GetItemsDelegate(int position, int bufferSize);

        public IEnumerator<T> GetEnumerator() {
            GetItemsDelegate getConnections = new GetItemsDelegate(GetItems);
            List<T>[] buffer = new List<T>[2];
            IAsyncResult[] asyncResult = new IAsyncResult[2];
            asyncResult[0] = getConnections.BeginInvoke(0, iteratorBufferSize, null, null);
            int nextBuffer = 0;
            for (int i = 0; i < Count; i += iteratorBufferSize) {
                Console.Write("Readloading the buffer..");
                DateTime startTime = DateTime.Now;
                buffer[nextBuffer] = getConnections.EndInvoke(asyncResult[nextBuffer]);
                nextBuffer = 1 - nextBuffer; // 0->1, 1->0
                asyncResult[nextBuffer] = getConnections.BeginInvoke(i + iteratorBufferSize, iteratorBufferSize, null, null);
                TimeSpan elapsed = DateTime.Now - startTime;
                Console.WriteLine("done.\t" + elapsed.Milliseconds + "ms");
                foreach (T c in buffer[1 - nextBuffer]) {
                    yield return c;
                }
            }
            getConnections.EndInvoke(asyncResult[nextBuffer]);
        }

        private List<T> GetItems(int position, int bufferSize) {
            List<T> ret = new List<T>();
            DataSet set = null;
            try {
                set = DataProxy.Instance.RunQuery(sqlQuery + " limit " + position + ", " + bufferSize);
                Type type = typeof(T);
                object[] parameters = new object[2];
                parameters[1] = IsGreedy;
                foreach (DataRow row in set.Tables["Table"].Rows) {
                    parameters[0] = row;
                    ret.Add((T)Activator.CreateInstance(type, parameters));
                }
            } catch (MySqlException ex) {
                ErrorLog.Log(@"Bad SQL Query:  "+sqlQuery);
                throw ex;
            }
            return ret;
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
            return GetEnumerator();
        }
    }
}
